﻿using System;
using System.Collections.Generic;
using System.IO;
using ComTypes = System.Runtime.InteropServices.ComTypes;

namespace NFox.Runtime.Com.Reflection
{

    /// <summary>
    /// Com程序集,只有类型集合,类型别名、枚举等未处理
    /// </summary>
    public class Assembly
    {

        /// <summary>
        /// 程序集字典
        /// </summary>
        private static Dictionary<AssemblyKey, Assembly> _openedAssemblies
            = new Dictionary<AssemblyKey, Assembly>();

        /// <summary>
        /// 获取IDispatch接口对象的类型信息
        /// </summary>
        /// <param name="obj">IDispatch接口对象</param>
        /// <returns>对象的类型信息</returns>
        public static Type GetType(IDispatch obj)
        {

            ComTypes.ITypeInfo info;
            obj.GetTypeInfo(0, 0, out info);
            ComTypes.ITypeLib lib;
            int id;
            info.GetContainingTypeLib(out lib, out id);

            IntPtr ip;
            lib.GetLibAttr(out ip);
            var la = Utils.GetObject<ComTypes.TYPELIBATTR>(ip);
            var key = 
                new AssemblyKey(
                    la.guid.ToString(),
                    la.wMajorVerNum, 
                    la.wMinorVerNum);
            lib.ReleaseTLibAttr(ip);
            if (!_openedAssemblies.ContainsKey(key))
            {
                string name, doc, helpfile;
                int hc;
                lib.GetDocumentation(-1, out name, out doc, out hc, out helpfile);
                _openedAssemblies.Add(key, new Assembly(key, name));
            }

            return _openedAssemblies[key].GetType(id, info);

        }

        static DirectoryInfo _root;

        private string _dir;

        private Assembly(AssemblyKey key, string name)
        {

            Key = key;
            Name = name;

            if (_root == null)
            {
                var assem =
                    System.Reflection.Assembly
                    .GetExecutingAssembly();
                var fi = new FileInfo(assem.Location);
                _root = fi.Directory.CreateSubdirectory("ComAssemblies");
            }
            _dir =
                _root
                .CreateSubdirectory(name)
                .CreateSubdirectory(key.ClsId.ToString())
                .CreateSubdirectory(key.Version)
                .FullName;

        }

        public string Name { get; }

        public AssemblyKey Key { get; }


        /// <summary>
        /// 类型字典
        /// </summary>
        public Dictionary<int, Type> Types { get; }
            = new Dictionary<int, Type>();

        /// <summary>
        /// 按索引获取Com程序集中的类型信息
        /// </summary>
        /// <param name="id">索引</param>
        /// <param name="info">ITypeInfo接口</param>
        /// <returns>对应索引的类型信息</returns>
        public Type GetType(int id, ComTypes.ITypeInfo info)
        {
            if (!Types.ContainsKey(id))
            {
                var name = $"{_dir}/{id}.typ";
                if (File.Exists(name))
                {
                    Types.Add(id, new Type(this, name));
                }
                else
                {
                    Type type = new Type(this, info);
                    type.Save(name);
                    Types.Add(id, type);
                }
            }
            return Types[id];
        }

        public override string ToString()
        {
            return Name;
        }

    }
}